<?php
namespace App\Trip\Orders\BookOrders\Scenic\Order;

use App\Models\Passengers\Passenger;
use App\Models\Passengers\Repositories\Interfaces\PassengerRepositoryInterface;
use App\Models\Scenic\ScenicSpot;
use App\Models\Trip\TripOrderCosts\TripOrderCost;
use App\Services\Scenic\ScenicQueryFactory;
use App\Trip\Enums\Scenic\ScenicProvider;
use App\Trip\Enums\TripTypes;
use App\Trip\IdentityCode\IdentityCode;
use App\Trip\Orders\BookOrders\Params\AbstractOrderParam\AbstractOrderParam;
use App\Trip\Orders\BookOrders\Params\BookOrderParam;
use App\Trip\Orders\TripNo;
use App\Trip\Orders\TripOrderState;
use App\Trip\Orders\TripType;
use App\Trip\Passengers\OrderSubPassenger;
use App\Trip\Passengers\Passengers;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;

class BookNewScenicOrder
{
    const ORDER_PARAM_FIELDS = [
        'scenic_id', 'ticket_id',
        'ticket_type', 'trip_type',
        'play_at','count'
    ];
    const PASSENGER_PARAM_FIELDS = [
        'passenger_id',
        'identity_code', 'identity_type',
        'truename', 'mobile',
    ];
    const CONTACT_PARAM_FIELDS = [
        'truename','passenger_id',
        'mobile', 'email',
        'identity_type', 'identity_code',
    ];

    private string $scenic_order_type = '';
    const PASSENGER_IDENTITY_TYPE = '1';
    private array $order_data;
    private array $passenger_data;
    private array $contact_data;
    private int $user_id;
    private ScenicProvider $provider;
    public function __construct(array $params, string $orderProvider = '')
    {
        $this->setOrderData($params['order'] ?? []);
        $this->setContactData($params['contact'] ?? []);
        $this->setPassengerData($params['passengers'] ?? []);
        $this->user_id = $params['user_id'] ?? 0;
        $this->scenic_order_type = TripTypes::SCENIC->value;
        //默认定义为途牛
        if (empty($orderProvider)){
            $this->provider = ScenicProvider::TUNIU;
        }else{
            $this->provider = ScenicProvider::from($orderProvider);
        }
    }


    private function setOrderData(array $param): static
    {
        $this->order_data = collect($param)->map(fn($passenger) => collect($passenger)->only(self::ORDER_PARAM_FIELDS)->toArray())->toArray();
        return $this;
    }

    private function setContactData(array $param): static
    {
        $this->contact_data = collect($param)->map(fn($passenger) => collect($passenger)->only(self::CONTACT_PARAM_FIELDS)->toArray())->toArray();
        return $this;
    }
    private function setPassengerData(array $param): static
    {
        $this->passenger_data = collect($param)->map(fn($passenger) => collect($passenger)->only(self::PASSENGER_PARAM_FIELDS)->toArray())->toArray();
        return $this;
    }


    public function getParamOrder(): array
    {
        $order_data            = $this->order_data;
        $valid_order_types  = $this->scenic_order_type;
        $validator             = Validator::make($order_data, [
            '*.scenic_id'       => "required",
            '*.ticket_id'   => "required",
            '*.ticket_type'  => "required",
            '*.trip_type'  => "required|in:{$valid_order_types}",
            '*.play_at'         => "required|date",
            '*.count'         => "required",
        ], [
            "*.trip_type.in"                         => "类型错误",
        ]);

        if ($validator->fails()) {
            Log::error("BookOrderManager:getParamOrder:{$validator->errors()->first()}");
            throw new \Exception($validator->errors()->first());
        }

        if (strtotime($order_data['play_at']) < strtotime(date('Y-m-d'))){
            throw new \Exception('游玩日期错误',404);
        }
        $order_data['play_at'] = date('Y-m-d',strtotime($order_data['play_at']));

        return $order_data;
    }

    public function getParamContact(bool $emailVerify, bool $requiredIdentity): array
    {
        $contact_data            = $this->contact_data;
        $valid_identity_types  = self::PASSENGER_IDENTITY_TYPE;
        $messages = [];
        if ($requiredIdentity){
            $rules = [
                '*.truename'       => "required",
                '*.identity_code'  => "required",
                '*.identity_type'  => "required|in:{$valid_identity_types}",
                '*.mobile'         => "required|phone",
            ];
            $messages = [
                "*.identity_type.in"                         => "证件类型错误",
                "*.identity_code.passengers_identify_number" => "证件号错误",
            ];
        }else{
            $rules= [
                '*.truename'       => "required",
                '*.mobile'         => "required|phone",
            ];
        }

        if ($emailVerify){
            $rules['*.email'] = "required|email";
        }

        $validator = Validator::make($contact_data, $rules, $messages);

        if ($validator->fails()) {
            Log::error("BookOrderManager:getParamOrder:{$validator->errors()->first()}");
            throw new \Exception($validator->errors()->first());
        }

        return $contact_data;
    }

    public function getParamPassengers(): array
    {
        $passengers            = $this->passenger_data;
        $valid_identity_types  = self::PASSENGER_IDENTITY_TYPE;
        $validator             = Validator::make($passengers, [
            '*.truename'       => "required",
            '*.passenger_id'   => "required",
            '*.identity_code'  => "required",
            '*.identity_type'  => "required|in:{$valid_identity_types}",
            '*.mobile'         => "required|phone",
        ], [
            "*.identity_type.in"                         => "证件类型错误",
            "*.identity_code.passengers_identify_number" => "证件号错误",
        ]);

        if ($validator->fails()) {
            Log::error("BookOrderManager:getPassengers:{$validator->errors()->first()}");
            throw new \Exception($validator->errors()->first());
        }

        /**
         * @var $passenger_info Passenger
         */
        $repeat_checks  = [];
        $passengerRepo  = app(PassengerRepositoryInterface::class);
        $passengers_tmp = [];
        foreach ($passengers as $key => $passenger) {
            $passenger_id = $passenger['passenger_id'];
            if (!isset($passengers_tmp[$passenger_id])) {
                $passenger_info = $passengerRepo->findOneByPassengerIdAndUserId($passenger_id, $this->user_id);
                if (!$passenger_info) {
                    throw new \Exception("游客不存在".":{$passenger['truename']}");
                }
                if ($passenger_info->getIdentityType() != 1){
                    throw new \Exception("游客证件类型只支持身份证");
                }
                $passengers_tmp[$passenger_id] = $passenger_info;
            } else {
                $passenger_info = $passengers_tmp[$passenger_id];
            }

            $passengers[$key]['sub_passenger_id'] = $passenger['passenger_id'];
            $passengers[$key]['passenger_id']     = $passenger_id;
            $passengers[$key]['sex']              = $passenger_info->getGender();
            $passengers[$key]['birth_day']        = $passenger_info->getBirthDay();
            $passengers[$key]['age']              = $passenger_info->getAge();
            $identity_code                        = $passenger_info->getIdentityCode();
            if ($passenger['identity_type'] != $passenger_info->getIdentityType()) {
                throw new \Exception("游客证件类型错误".":{$passenger['truename']}");
            }

            if ($passenger_info->getTrueName() != $passenger['truename']) {
                throw new \Exception("游客姓名发生变化".":{$passenger['truename']}");
            }

            if (1 == $passenger['identity_type'] && $identity_code !== $passenger['identity_code']) {
                $idc = new IdentityCode($identity_code);
                if (!($idc->hide(2, 2) == $passenger['identity_code'] || $idc->hide(6, 2) == $passenger['identity_code'])) {
                    throw new \Exception("游客证件号发生变化".":{$passenger['truename']}");
                }
            }
            $passengers[$key]['identity_code'] = $identity_code;
            // 验证(成人、学生、残|军)乘客是否重复
            if (!in_array($passenger['passenger_type'], Passengers::ChildrenTypes())) {
                if (isset($repeat_checks[$passenger_id])) {
                    throw new \Exception("游客重复".":{$passenger['truename']}");
                }
                $repeat_checks[$passenger_id] = $passenger_id;
            }

        }

        return $passengers;
    }


    public function buildOrderInfo(): array
    {
        $order_data = $this->getParamOrder();
        $order_params = new BookOrderParam($order_data);
        $scenicQuery = (new ScenicQueryFactory())->findOneByProvider($this->provider);
        $ticket_data = $scenicQuery->ticket($order_data['ticket_id']);
        $passengers = [];
        if (in_array($ticket_data['ticket']['book']['passenger_limit'],[2,3,6,7])){
            $passengers = $this->getParamPassengers();
        }
        if ($ticket_data['ticket']['ticket_delivery'] == 1){
            throw new \Exception('暂不支持实体票出售',403);
        }
        //数量限制
        if ($ticket_data['ticket']['book']['min_num'] > $order_data['count']){
            throw new \Exception('最少购买'.$ticket_data['ticket']['book']['min_num'].'张门票',405);
        }
        if ($ticket_data['ticket']['book']['max_num'] < $order_data['count']){
            throw new \Exception('最多购买'.$ticket_data['ticket']['book']['max_num'].'张门票',405);
        }
        $contactVerify = false;
        $emailVerify = false;
        if (in_array($ticket_data['ticket']['enter_type'],[205,302])){
            $emailVerify = true;
        }
        $contact = $this->getParamContact($emailVerify,$contactVerify);

        $play_at = $order_data['play_at'];
        $calendars= $ticket_data['calendar'] ?? [];
        $calendarDateMap = [];
        foreach ($calendars as $calendar){
            $calendarDateMap[$calendar['date']] = [
                'date' => $calendar['date'],
                'price' => $calendar['price'],
                'child_price' => $calendar['child_price'],
            ];
        }
        if (empty($calendarDateMap[$play_at])){
            throw new \Exception('游玩日期错误,不存在对应时间点门票',404);
        }
        $price_details = $this->priceDetailsBuilder($ticket_data['ticket']['ticket_type'], $ticket_data['ticket']['ticket_name'], $this->order_data['count'],$calendarDateMap[$play_at]['price']);
        $order_data['usage'] = $ticket_data['ticket']['book']['notice'];
        $order_data['enter_type'] = $ticket_data['ticket']['enter_type'];
        $order_data['ticket_name'] = $ticket_data['ticket']['ticket_name'];
        $order_data['ticket_office'] = $ticket_data['ticket']['draw_address'];
        $order_data['ticket_delivery'] = $ticket_data['ticket']['ticket_delivery'];
        $order_data['ticket_type'] = $ticket_data['ticket']['ticket_type'];
        $scenicSpot = ScenicSpot::where('scenic_id',$order_data['scenic_id'])->first();
        if ($scenicSpot instanceof ScenicSpot){
            $order_data['scenic_name'] = $scenicSpot->scenic_name;
        }
        return [
            'order_info'=>[
                'trip_id' => $order_params->getTripId(),
                'channel_id' => channel()->getChannelId(),
                'order_sn' => $order_params->generateOrderSn(),
                'out_order_sn' => $order_params->getOutOrderSn(),
                'user_id' => $this->user_id,
                'transfer_flag' => $order_params->getTransferFlag(),
                'trip_no' => $order_params->getTripNo(),
                'order_service' => $this->provider->value,
                'trip_at' => $play_at,
                'trip_type' => $this->scenic_order_type,
                'order_state' => TripOrderState::ORDER_STATE_WAITING_PAY,
            ],
            'passengers'=>$passengers,
            'order_params'=>$order_data,
            'contact'=>$contact,
            'price_details'=>$price_details,
        ];
    }

    /**
     * @param int $type
     * @param string $name
     * @param int $count
     * @param $price
     * @return array
     */
    public function priceDetailsBuilder(int $type,string $name,int $count,$price) :array
    {
        $details                = [];
        $total_price            = bcmul($price,$count,2);
        $details[] = [
            'type' => $type,
            'name' => $name,
            'name_uy' => trans2uy($name),
            "total_price" => $total_price,
            'count' => $count,
        ];

        return [
            'total_price'=>$total_price,
            'details'=>$details
        ];
    }
}